//===============================================================================
// Copyright (C) 2008 Charles River Media. All rights reserved.
// 
// Catmull-Clark subdivision surface viewer program
//
// The developer of the code is
// Gyorgy Antal <gyantal@gmail.com>
//
// Contributor(s):
// Laszlo Szirmay-Kalos <szirmay@iit.bme.hu>
//
// The software was developed for the chapter 
// "Fast Evaluation of Subdivision Surfaces on DirectX10 Graphics Hardware"
// of the book ShaderX 6: Advanced Rendering Techniques, 
// edited by Wolfgang Engel,
// the appropriate section edited by Tom Forsyth. 
//
// The files are provided without support, instruction, or implied warranty of any
// kind.  Charles River Media makes no guarantee of its fitness for a particular purpose and is
// not liable under any circumstances for any damages or loss whatsoever arising
// from the use or inability to use this file or items derived from it.
//===============================================================================
// File: Scanner.cpp
//
// Description:
//===============================================================================

#include "scanner.h"
#include <stdlib.h>

//---------------------------------------------------------------
Token SpecialCharTable :: FindToken(char c) {
//---------------------------------------------------------------
    for(int i = 0; i < nspecials; i++) {
		if (specials[i].Match(c)) return specials[i].GetToken();
    }
    return ERROR_TOKEN;
}

//---------------------------------------------------------------
Token KeywordTable :: FindToken(char * key) {
//---------------------------------------------------------------
	for(int i = 0; i < nkeys; i++) {
		if (keywords[i].Match(key)) return keywords[i].GetToken();
    }
    return ERROR_TOKEN;
}

//---------------------------------------------------------------
inline Token KeywordTable :: FindNumToken(int tok) {
//---------------------------------------------------------------
	return (Token)tok;
}

//---------------------------------------------------------------
char * KeywordTable :: FindKeyword(Token tok) {
//---------------------------------------------------------------
    for(int i = 0; i < sizeof keywords / sizeof(Keyword); i++) {
		if (keywords[i].Match(tok)) return keywords[i].GetKeyword();
    }
    return NULL;
}

//-------------------------------------------------------------------
inline void TokenBuffer :: Put(char data) {
//-------------------------------------------------------------------
    if (buff_ptr == MAXTOKENBUFFER - 1)
		exit(-1);
		//cerr << "Token Buffer overflow";
    else    buffer[ buff_ptr++ ] = data;
}

/*****************************************************************
*   SCANNER STATE MACHINE
*   Separates input stream into tokens
******************************************************************/

//---------------------------------------------------------------
// Read next character from input stream and advance
//---------------------------------------------------------------
char Scanner :: Read() {
//---------------------------------------------------------------
    char c = InputFile :: Read();
//
// Follow position to give proper error messages
// 
    switch (c) {
    case '\n': line_pos++; char_pos = 0; 
               break; 
    default:   char_pos++; 
               break;
    }
    return c;
}

//---------------------------------------------------------------                       
// Scans input file and separates items as tokens                
// out: next token                                               
//---------------------------------------------------------------
Token Scanner :: GetToken () {
//----------------------------------------------------------------
    token_buffer.Clear();

    current_token = EOF_TOKEN;
//
// GET CHARACTERS UNTIL A TOKEN IS COMPLETE OR EOF
//
    while (! IsEOF(curr_char)) {
        curr_char = Read();
//
// LINE, ROW
//
		if (curr_char == '\n') {
			line_pos++;
			char_pos = 0;
		} else char_pos++;
//
// WHITESPACE CHARACTERS -> IGNORE
//
		if (IsWhite(curr_char)) continue;
//
// COMMENTS
//
		if (IsCommentBegin(curr_char)) {
			Read();		
            while (!IsCommentEnd(curr_char)) curr_char = Read();
		}
//
// SPECIAL CHARACTERS
//
       
		current_token = special_chars.FindToken(curr_char);
		if (current_token != ERROR_TOKEN) goto end;
//
// NUMBERS
//
        if (IsMinus(curr_char)) {
			token_buffer.Put(curr_char);
            curr_char = Read();
        }
        if (IsDecimal(curr_char)) {
            token_buffer.Put(curr_char);
            BOOL real = FALSE;
            for(; ;) {
				next_char = InspectNext();
                if (IsDecimal(next_char)) {
					token_buffer.Put(next_char);
                    Advance();
                } else if (IsDot(next_char) && ! real) {
					real = TRUE;
                    token_buffer.Put(next_char);
                    Advance();
                } else {
                    current_token = NUMBER_TOKEN;
                    goto end;
                }
            }
        }
//
// NAMES
//
        if (IsLetter(curr_char)) {
		    token_buffer.Put(curr_char);
            for(; ;) {
				next_char = InspectNext();
                if (!IsLetter(next_char)) {
//
// CHECK IF RESERVED WORD
//
					current_token = keys.FindToken(token_buffer.Get());
					if (current_token == ERROR_TOKEN) current_token = NAME_TOKEN;
					goto end;
                } else {
                    token_buffer.Put(next_char);
                    Advance();
                }
			}
        }
//
// END OF FILE
///
    }
    current_token = EOF_TOKEN;
end:
    return current_token;
}

//---------------------------------------------------------------
//  Compare the given token to the actual and 
//  If they match get the next token
//---------------------------------------------------------------
void Scanner :: Match(Token t) {
//---------------------------------------------------------------
    if (current_token == t)                  GetToken();
    else if (current_token != EOF_TOKEN)     Error("Unexpected Token");
}
    
//---------------------------------------------------------------
char * Scanner :: GetName() {
//---------------------------------------------------------------
    static char buffer[MAXTOKENBUFFER];
	//strcpy(buffer, token_buffer.Get());
	strcpy_s(buffer, MAXTOKENBUFFER, token_buffer.Get());
    Match(current_token);
    return buffer;
}

//---------------------------------------------------------------
Number Scanner :: GetNumber() {
//---------------------------------------------------------------
    Number num = atol(token_buffer.Get());
    Match(NUMBER_TOKEN);
    return num;
}

//---------------------------------------------------------------
float Scanner :: GetReal() {
//---------------------------------------------------------------
    float d = (float)atof(token_buffer.Get());
    Match(NUMBER_TOKEN);
    return d;
}

//-----------------------------------------------------------------
void Scanner :: Error(char * mess) {
//-----------------------------------------------------------------
//
// GET THE LOCATION OF THE ERROR
//
  /* cerr << "\nERROR : " << mess <<", " << token_buffer.Get();
   cerr << " IN FILE: " << name;
   cerr << " IN LINE: " << line_pos + 1;
   cerr << " LOCATION: " << char_pos + 1 << '\n';  */
   exit(-1);
}
